package oss.juelg.flashlight_mini;

import static oss.juelg.flashlight_mini.FlashlightActivity.FLASH_INTENT_STRING;
import static oss.juelg.flashlight_mini.FlashlightActivity.RELEASE_POLICY_INTENT_STRING;
import static oss.juelg.flashlight_mini.FlashlightActivity.START_INTENT_STRING;
import static oss.juelg.flashlight_mini.FlashlightActivity.TAG;

import java.util.concurrent.LinkedBlockingQueue;

import oss.juelg.flashlight_mini.FlashState.FlashMode;
import oss.juelg.flashlight_mini.FlashState.ReleasePolicy;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

/**
 * 
 * @author Chris
 * 
 */
public class FlashlightService extends Service {

	FlashState prevState;

	Thread cameraThread;

	// worker thread receives commands using stateList
	private LinkedBlockingQueue<FlashState> stateList;

	public FlashlightService() {
		super();
		// stop service after some timeout? would conflict with widget?
		Log.v(TAG, "FlashlightService() " + toString());

		stateList = new LinkedBlockingQueue<FlashState>();
		prevState = new FlashState(false, ReleasePolicy.EAGER);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		if (intent == null) {
			Log.d(TAG, "onStartCommand(Intent: " + intent + ")");
			return Service.START_NOT_STICKY;
		}

		Log.d(TAG,
				"onStartCommand(Intent: " + intent + ", data: "
						+ intent.getDataString() + ")");
		if (intent.getExtras() != null) {
			Log.d(TAG, "onStartCommand(Intent extras size: "
					+ intent.getExtras().size());
			Log.d(TAG, "onStartCommand(Intent extra policy: "
					+ intent.getExtras().getSerializable("policy"));
			Log.d(TAG, "onStartCommand(Intent extra flash: "
					+ intent.getExtras().getSerializable("flash"));
		}

		if (cameraThread == null || !cameraThread.isAlive()) {
			Log.e(TAG, "onStartCommand(): cameraThread not running: "
					+ cameraThread);
			startCameraThread();
		}

		// FIXED: Intents with data Uri attached dont match simple Intent Filter

		// intent "toggle"
		if (intent.getAction().equals(FLASH_INTENT_STRING)) {
			// TODO read intent data, should be toggle

			if (intent.getExtras() != null
					&& intent.getExtras().getSerializable("flash")
							.equals(FlashMode.TOGGLE)) {

				transferState(new FlashState(!prevState.mode, prevState.policy));
			} else {
				Log.e(TAG, "onStartCommand(Intent extra flash: "
						+ intent.getExtras().getSerializable("flash")
						+ ", expected TOGGLE");
			}
		} else if (intent.getAction().equals(START_INTENT_STRING)) {

			transferState(new FlashState(true, ReleasePolicy.LAZY));
			// stopping and restarting the service should work cleanly

		} else if (intent.getAction().equals(RELEASE_POLICY_INTENT_STRING)) {

			ReleasePolicy policy = (ReleasePolicy) intent.getExtras().getSerializable("policy");
			transferState(new FlashState(prevState.mode, policy));
			
		} else {
			Log.e(TAG, "unknown intent received");
		}

		Log.d(TAG, "onStartCommand() done");
		return Service.START_STICKY;
	}

	private void transferState(FlashState newState) {
		try {
			stateList.clear();
			stateList.put(newState);
		} catch (InterruptedException e) {
			Log.e(TAG, "interrupted", e);
		}
		prevState = newState;
	}

	@Override
	public void onCreate() {
		super.onCreate();
		Log.d(TAG, getClass().getName() + " onCreate()");
		Log.d(TAG, "onCreate(): thread: " + cameraThread + ", stateList: "
				+ stateList);

		startCameraThread();
	}

	private void startCameraThread() {
		if (cameraThread == null || !cameraThread.isAlive()) {
			stateList.clear();
			cameraThread = new Thread(new CameraThread(stateList), "cameraThread"
					+ System.currentTimeMillis());
			cameraThread.start();
		}
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		Log.d(TAG, getClass().getName() + " onDestroy()");
		if (cameraThread != null) {
			cameraThread.interrupt();
			try {
				cameraThread.join();
			} catch (InterruptedException e) {
				Log.e(TAG, getClass().getName()
						+ " onDestroy() join interrupted", e);
			}
			Log.d(TAG, getClass().getName() + " onDestroy() join successful");
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		Log.v(TAG, "onBind: " + intent.getAction());
		return null; // no binding
	}
}
